/*
    Speedbump aka Project Kimbula
    Copyright (C) 2010  Ping Shin Ching

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see http://www.gnu.org/licenses.

	Ping Shin Ching
	righteous.ninja@gmail.com OR ping_sc@hotmail.com
	More information at http://www.tidytutorials.com/p/speedbump.html
 */

#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include "ratelimiter.h"



RateLimitFilter::RateLimitFilter(){

}

/*************************************************************************
 * Initialise the Rate Limiter filter with the threshold the percentage
 * drop in rate and the time period to hold the dropped rate
 *************************************************************************/
int RateLimitFilter::InitFilter(InitInterface* fip){
	const char* fir = fip->GetInitParameter("ratelimit-msg");
	
	numberofratedips=0;
	ratedips=0;
	resetperiod			= atol((const char*)fip->GetInitParameter("throttleperiod")); 
	bytethreshhold		= strtoul((const char*)fip->GetInitParameter("throttlethreshhold"),0,0), 
	conntheshhold		= atol((const char*)fip->GetInitParameter("throttleconnections"));
	throttlepercentage	= atoi((const char*)fip->GetInitParameter("throttlepercent"));
	
	if((fir)){
		filterRedirect	= (char*)malloc(strlen(fir)+1);
		memcpy(filterRedirect, fir, strlen(fir)+1);
	}
	else
		filterRedirect=0;

	return 0;
}

/*************************************************************************
 * Get the filter name
 *************************************************************************/
const char* RateLimitFilter::GetFilterName(){
	return "Rate-Limiter";
}

/*************************************************************************
 * Get the redirect url
 *************************************************************************/
const char*	RateLimitFilter::GetFilterRedirect(){
	return filterRedirect;
}

/*************************************************************************
 * Get the filter type - When it is to be called
 *************************************************************************/
int RateLimitFilter::GetFilterType(){
	return FILTER_MSG_BYTES|FILTER_FREE|FILTER_START;
}

/*************************************************************************
 * Destructor
 *************************************************************************/
RateLimitFilter::~RateLimitFilter(){
	flogit()<<"Ratelimiter Freeing"<<flend;
	if(ratedips){
		free(ratedips);
		ratedips=0;
	}
	if(filterRedirect)
		free(filterRedirect);
}

/*************************************************************************
 * The actual filter function
 *************************************************************************/
int RateLimitFilter::DoFilter(const char * ip, FILTER_TYPE type, FilterData* param){
	
	int i;

	int retvalue=0;
	
	char* x = (char*)param;
	FilterData * fd;
	
	time_t now;

	long oldesttime;
	long oldestindex;
	
	unsigned int up, down, msgtype;

	now	= time(0);
	oldesttime = now;
	oldestindex	= -1;


	switch(type){
		case FILTER_START:
			fd = (FilterData*)param;
				
		   	if(ip==0)
				return 0;


			for( i = 0 ; i < numberofratedips ; i++){ 

				if(ratedips[i].breachedtime !=0 && (now - ratedips[i].breachedtime) > resetperiod){
					flogit()<<"Ratelimit reset2 "<<ip<<flend;
					ratedips[i].bytestranferred=0;
					ratedips[i].breachedtime=0;
					ratedips[i].numberofconnections=0;
				}

				if(now - ratedips[i].lastupdatedtime > resetperiod && ratedips[i].lastupdatedtime < oldesttime){
					oldesttime = ratedips[i].lastupdatedtime;
					oldestindex = i;
				}

				if(FilterChain::CompareTwoIPs(ip, (const char*)ratedips[i].ipaddress)){

					ratedips[i].numberofconnections++;

					if(	(bytethreshhold!=0 &&  ratedips[i].bytestranferred > bytethreshhold) || 
						(conntheshhold!=0 && ratedips[i].numberofconnections > conntheshhold)){
						if(throttlepercentage!=0){
							flogit()<<"Ratelimiting "<<ip<<" "<<ratedips[i].bytestranferred<<" "<<ratedips[i].numberofconnections<<flend;
							fd->ratelimitup = (unsigned int)((float)(throttlepercentage*fd->ratelimitup))/100.0;
							fd->ratelimitdown = (unsigned int)((float)(throttlepercentage*fd->ratelimitdown))/100.0;
							retvalue=1;
						}
						else{
							flogit()<<"Ratelimit over "<<ip<<" "<<ratedips[i].bytestranferred<<" "<<ratedips[i].numberofconnections<<flend;
							retvalue=0;
						}

						if(ratedips[i].breachedtime==0){
							ratedips[i].breachedtime=now;
						}

					}
					else{
						flogit()<<"Rate ok "<< ip<<" "<<ratedips[i].bytestranferred<<" "<<ratedips[i].numberofconnections<<flend;
						retvalue=1;
					}

					goto THE_END;
				}

			}

			if(numberofratedips+1 > MAX_RATELIMIT_IPS ){
				flogit()<<"Ratelimit arraysize reached"<<flend;
				//printf("%s\n", tmps64);

				if(oldestindex!=-1){
					flogit()<<"Ratelimit "<< ip <<" replaced with "<< ratedips[oldestindex].ipaddress <<flend;

					FilterChain::AssignIP((char*)ratedips[oldestindex].ipaddress, FILTER_ADDRSTRLEN, ip);
					ratedips[oldestindex].lastupdatedtime	= now;
					ratedips[oldestindex].bytestranferred	= 0;
					ratedips[oldestindex].breachedtime		= 0;
					ratedips[oldestindex].numberofconnections=1;
				}
				else{
					flogerr()<<"Ratelimiter auto pass"<<flend;
				}
				retvalue=1;
			}
			else{
				flogit()<<"Ratelimit new "<<ip<<" "<<numberofratedips<<flend;

				if(ratelimiterRealloc()!=0){

					FilterChain::AssignIP((char*)ratedips[numberofratedips].ipaddress, FILTER_ADDRSTRLEN, ip);
		
					ratedips[numberofratedips].breachedtime		= 0;
					ratedips[numberofratedips].lastupdatedtime	= now;
					ratedips[numberofratedips].bytestranferred	= 0;
					ratedips[numberofratedips].numberofconnections=1;

					numberofratedips++;
				}
				retvalue = 1;

			}
			
			
			break;

		case FILTER_END:
			retvalue=1;
			break;

		case FILTER_MSG_BYTES:
			char* iptmp[INET6_ADDRSTRLEN];
			FilterChain::DecodeMessage(x, &msgtype, (char*)iptmp, &up, &down);
			
			for( i = 0 ; i < numberofratedips ; i++){ 
				if(FilterChain::CompareTwoIPs((const char*)iptmp, (const char*)ratedips[i].ipaddress)){
					flogit()<<"Ratelimit msg "<<ip<<flend;
					if(ratedips[i].breachedtime==0){
						ratedips[i].bytestranferred+=(up+down);
						ratedips[i].lastupdatedtime=now;
					}
					retvalue=1;					
					goto THE_END;
				}
			}
			break;

		case FILTER_FREE:
			/*Not used anymore*/
			retvalue = 1;
			break;
		default:
			break;
	}

THE_END:
	return retvalue;
}

/*************************************************************************
 * RateLimiter realloc function for IPs
 *************************************************************************/
int RateLimitFilter::ratelimiterRealloc(){
	RatedIP* tempp;
	if((tempp = (RatedIP*)realloc(ratedips, (numberofratedips+1)*sizeof(RatedIP)))==0){
		return 0;
	}
	else{
		ratedips = tempp;
		return 1;
	}
}
